home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************************************
- *
- *
- * MacZoop - "the framework for the rest of us"
- *
- *
- *
- * ZFolderScanner.cpp -- a generic object for recursively searching folders
- *
- *
- *
- *
- *
- * Ā© 1996, Graham Cox
- *
- *
- *
- *
- *************************************************************************************************/
-
-
- #include "ZFolderScanner.h"
- #include "ZProgress.h"
- #include "MacZoop.h"
- #include "FileMgrUtils.h"
-
- /*--------------------------------*** CONSTRUCTOR ***---------------------------------*/
-
- ZFolderScanner::ZFolderScanner( const FSSpec& rootFolder )
- : ZFile( rootFolder )
- {
- Boolean isDirectory;
-
- classID = CLASS_ZFolderScanner;
-
- // check that the spec is a folder- throws exception if not.
-
- FailOSErr( FSpGetDirectoryID ( &itsSpec, &topDirID, &isDirectory ));
- FailOSErr( (isDirectory)? noErr : paramErr );
-
- curDepth = 0;
- searchDepth = kDefaultSearchDepth;
- useProgressDialog = TRUE;
- }
-
- /*--------------------------------*** CONSTRUCTOR ***---------------------------------*/
-
-
- ZFolderScanner::ZFolderScanner()
- : ZFile( "\p" )
- {
- // assume the filespec for the start-up hard disk.
-
- classID = CLASS_ZFolderScanner;
-
- HParamBlockRec hb;
-
- hb.volumeParam.ioCompletion = NULL;
- hb.volumeParam.ioVolIndex = 1;
- hb.volumeParam.ioNamePtr = fName;
- fName[0] = 0;
-
- FailOSErr( PBHGetVInfoSync( &hb ));
-
- // make a filespec. This is a volume, so to make sure all goes
- // OK, we actually force the volume name to have an appended colon
- // and set the parent ID to -2.
-
- itsSpec.vRefNum = hb.volumeParam.ioVRefNum;
- itsSpec.parID = -2;
- BlockMoveData(fName, itsSpec.name, fName[0] + 1);
- itsSpec.name[fName[0] + 1] = ':';
- itsSpec.name[0] += 1;
-
- curDepth = 0;
- searchDepth = kDefaultSearchDepth;
- useProgressDialog = TRUE;
- }
-
-
- /*---------------------------------*** DESTRUCTOR ***---------------------------------*/
-
- ZFolderScanner::~ZFolderScanner()
- {
- }
-
-
- /*------------------------------*** SETSEARCHDEPTH ***-------------------------------*/
- /*
- set how many levels you want to recurse to when scanning. The default is 0, meaning only
- scan the folder and no folders within it. Passing -1 means search every folder no matter
- how deep the search goes. Other positive values set the scanning depth.
- ---------------------------------------------------------------------------------------*/
-
- void ZFolderScanner::SetSearchDepth( const short aSearchDepth )
- {
- searchDepth = aSearchDepth;
- }
-
- /*--------------------------------*** PICKFOLDER ***---------------------------------*/
- /*
- presents an interface for selecting a folder to scan
- ---------------------------------------------------------------------------------------*/
-
- Boolean ZFolderScanner::PickFolder()
- {
- FSSpec aSpec;
- Boolean result = FALSE;
-
- if( ChooseFolder( &aSpec ))
- {
- itsSpec = aSpec;
- result = TRUE;
- }
-
- return result;
- }
-
-
- /*--------------------------------*** SCANFOLDER ***---------------------------------*/
- /*
- public call to kick off the folder scan after setting it up.
- ---------------------------------------------------------------------------------------*/
-
- void ZFolderScanner::ScanFolder()
- {
- Boolean isDirectory;
-
- // set up the <pb> data member for scanning
-
- pb.hFileInfo.ioNamePtr = fName;
- pb.hFileInfo.ioVRefNum = itsSpec.vRefNum;
- fName[0] = 0;
- curDepth = 0;
-
- FailOSErr( FSpGetDirectoryID( &itsSpec, &topDirID, &isDirectory ));
- FailOSErr( (isDirectory)? noErr : paramErr );
-
- // set up progress dialog, if wanted
-
- if( useProgressDialog )
- {
- FailNIL( itsPD = new ZProgress( gApplication, kStdProgressResID, _STRIPED_BAR, kIndeterminateProgress, kStopType ));
- itsPD->SetTitle( itsSpec.name );
- }
-
- // call Scan1Folder, which recurses to the set depth if need be.
-
- try
- {
- Scan1Folder( topDirID );
- }
- catch( OSErr err )
- {
- // an error occurred, or the user hit "Stop"
-
- if( useProgressDialog )
- ForgetObject( itsPD );
-
- // main exception handler doesn't display alert for cancel message,
- // so it is quite safe to throw it in any case
-
- throw err;
- }
-
- if( useProgressDialog )
- ForgetObject( itsPD );
- }
-
-
- /*-------------------------------*** SCAN1FOLDER ***---------------------------------*/
- /*
- internal method that calls itself recursively to implement a scan of folders more than
- 1 level deep. THe recursion depth can be controlled by the searchDepth data member.
- ---------------------------------------------------------------------------------------*/
-
- void ZFolderScanner::Scan1Folder( const long dirID )
- {
- short index = 1;
- OSErr theErr;
-
- curDepth++; // gone down a level.
-
- // for each item in the folder passed, either pass it to Process1File, or call
- // this method again recursively.
-
- do
- {
- pb.dirInfo.ioFDirIndex = index++; // iterate through this folder
- pb.dirInfo.ioDrDirID = dirID; // this is the folder to look in
-
- theErr = PBGetCatInfoSync( &pb );
-
- // if the err is 'fnfErr', then this only means we tried to get more files than
- // there were in the folder. Thus this is not something we should throw.
-
- if (( theErr != noErr ) && ( theErr != fnfErr ))
- FailOSErr( theErr );
-
- // what have we got there?
-
- if( theErr == noErr )
- {
- FSSpec aSpec;
-
- if (( pb.hFileInfo.ioFlAttrib & ioDirMask ) != 0 )
- {
- // another folder within this one- recurse if we have depth in hand. If
- // search depth is -1, we search as deep as necessary.
-
- if (( curDepth < searchDepth ) ||
- ( searchDepth == kScanEveryFolderInHierarchy ))
- {
- FailOSErr( FSMakeFSSpec( itsSpec.vRefNum, dirID, fName, &aSpec ));
-
- Process1Folder( aSpec );
- Scan1Folder( pb.dirInfo.ioDrDirID );
- }
- }
- else
- {
- // a file, so call the method to handle the file. First we make a nice FSSpec
- // for the file in case we want to make an ZFile object, etc.
-
- FailOSErr( FSMakeFSSpec( itsSpec.vRefNum, dirID, fName, &aSpec ));
- Process1File( aSpec, pb.hFileInfo.ioFlFndrInfo.fdType );
- }
- }
- }
- while( theErr == noErr );
-
- // leaving, so decrement the level counter
-
- --curDepth;
- }
-
- /*------------------------------*** PROCESS1FOLDER ***-------------------------------*/
- /*
- called for every new folder before it is scanned. The default method does nothing, but
- you may want to override it for special uses.
- ---------------------------------------------------------------------------------------*/
-
-
- void ZFolderScanner::Process1Folder( const FSSpec& aSpec )
- {
- SendMessage( msgFolderscanProcess1Folder, (void*) &aSpec );
- }
-
-
- /*-------------------------------*** PROCESS1FILE ***--------------------------------*/
- /*
- called for every file in the scanned folders. Override to do something useful with the
- file.
- ---------------------------------------------------------------------------------------*/
-
- void ZFolderScanner::Process1File( const FSSpec& aSpec, const OSType fType )
- {
- // this method is called for every file found in the folder search. The default method
- // doesn't do anything, but you will override this to process those files of interest.
- // Note that folders are not passed to this function, only files. A common action at
- // this point is to make a ZFile object with the FSSpec, then read the file, etc.
- // Naturally your overridden method can really do what it likes. If you are only interested
- // in particular file types, for example, you can simply ignore those that you don't want.
- // if you want to use the progress dialog, you can call the inherited method to maintain
- // it. By default, this passes each filename as a message, and uses the "barber-pole" style.
-
- if( useProgressDialog )
- {
- itsPD->SetMessage( aSpec.name );
- FailOSErr( itsPD->InformProgress( 0 )? noErr : userCanceledErr );
- }
-
- SendMessage( msgFolderscanProcess1File, (void*) &aSpec );
- }
-
-
-
- #pragma mark -
-
-
-
- Boolean ChooseFolder( FSSpec* folderSpec )
- {
- Boolean result = FALSE;
- OSErr err;
-
- #if _USE_NAVIGATION_SERVICES
-
- if ( gMacInfo.hasNavigationServices )
- {
- // use groovy new folder selector dialog...
-
- NavDialogOptions navOptions;
- NavReplyRecord navReply;
-
- FailOSErr( NavGetDefaultDialogOptions( &navOptions ));
- gApplication->GetName( navOptions.clientName );
- navOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles;
- gWindowManager->Deactivate();
-
- err = NavChooseFolder( NULL,
- &navReply,
- &navOptions,
- gNavEventHandler,
- NULL,
- 0L );
-
- gWindowManager->Activate();
-
- if (( err == noErr ) && navReply.validRecord )
- {
- // extract the FSSpec:
-
- AEDesc specDesc;
- FSSpec dirSpec;
- CInfoPBRec pb;
-
- FailOSErr( AEGetNthDesc( &navReply.selection, 1, typeFSS, NULL, &specDesc ));
- BlockMoveData( *specDesc.dataHandle, &dirSpec, sizeof( FSSpec ));
-
- // to return the FSSpec in a compatible way, we need to do more than simply
- // return this spec. The <parID> field of <dirSpec> is the directory ID of the
- // chosen folder- what this function is supposed to return is the full FSSpec:
-
- pb.dirInfo.ioNamePtr = folderSpec->name;
- pb.dirInfo.ioVRefNum = dirSpec.vRefNum;
- pb.dirInfo.ioCompletion = NULL;
- pb.dirInfo.ioFDirIndex = -1;
- pb.dirInfo.ioDrDirID = dirSpec.parID;
-
- err = PBGetCatInfoSync( &pb );
-
- folderSpec->parID = pb.dirInfo.ioDrParID;
- folderSpec->vRefNum = pb.dirInfo.ioVRefNum;
-
- result = TRUE;
- }
-
- FailOSErr( NavDisposeReply( &navReply ));
- }
- else
- {
- #endif
-
- Point where = {-1, -1};
- FileFilterYDUPP ffUPP;
- DlgHookYDUPP dhUPP;
- tFolderInfo folderInfo;
- short dummyActiveList = 0;
-
- gWindowManager->DeactivateForDialog( kPickFolderDialogID );
-
- ffUPP = NewFileFilterYDProc((ProcPtr) GetDirFileFilter);
- dhUPP = NewDlgHookYDProc((ProcPtr) GetDirDlgHook);
-
- folderInfo.selectHit = FALSE;
- folderInfo.dirFlag = FALSE;
-
- CustomPutFile("\p","\p",
- &folderInfo.aReply,
- kPickFolderDialogID,
- where,
- dhUPP,
- NULL,
- &dummyActiveList,
- NULL,
- &folderInfo);
-
- DisposeRoutineDescriptor(ffUPP);
- DisposeRoutineDescriptor(dhUPP);
-
- if (folderInfo.selectHit)
- {
- if ( !folderInfo.aReply.sfIsFolder && !folderInfo.aReply.sfIsVolume )
- {
- FSSpec tempSpec;
- CInfoPBRec infoPB;
- Boolean isDirectory;
-
- tempSpec = folderInfo.aReply.sfFile;
-
- if (tempSpec.name[0] != '\0')
- return FALSE; //err
-
- infoPB.dirInfo.ioNamePtr = tempSpec.name;
- infoPB.dirInfo.ioVRefNum = tempSpec.vRefNum;
- infoPB.dirInfo.ioDrDirID = tempSpec.parID;
- infoPB.dirInfo.ioFDirIndex = -1;
-
- err = PBGetCatInfoSync( &infoPB );
- if (err)
- return FALSE;
-
- tempSpec.parID = infoPB.dirInfo.ioDrParID ;
-
- // make sure that it's a directory
-
- isDirectory = (infoPB.dirInfo.ioFlAttrib & 0x10) ;
- if ( !isDirectory )
- return FALSE; //err
-
- folderInfo.aReply.sfFile = tempSpec ;
- folderInfo.aReply.sfScript = infoPB.dirInfo.ioDrFndrInfo.frScript ;
- folderInfo.aReply.sfFlags = infoPB.dirInfo.ioDrUsrWds.frFlags ;
- folderInfo.aReply.sfIsFolder = (tempSpec.parID == 1) ? (0x00) : (0xFF) ;
- folderInfo.aReply.sfIsVolume = (tempSpec.parID == 1) ? (0xFF) : (0x00) ; ;
- }
-
- *folderSpec = folderInfo.aReply.sfFile;
-
- gWindowManager->Activate();
- result = TRUE;
- }
-
- #if _USE_NAVIGATION_SERVICES
- }
- #endif
-
- return result;
- }
-
- /*****************************************************************************/
-
-
- static pascal Boolean GetDirFileFilter(ParmBlkPtr pb,tFolderInfo* fInfo)
- {
- return ((pb->fileParam.ioFlAttrib & ioDirMask) == 0);
- }
-
- /*****************************************************************************/
-
-
- static pascal short GetDirDlgHook(short item, DialogPtr theDialog, tFolderInfo* fInfo)
- {
- short itemType;
- Handle itemHand;
- Rect itemBox;
- FSSpec fRef;
- static FSSpec lastFile;
-
- if (GetWRefCon(theDialog) == (long) sfMainDialogRefCon)
- {
- fRef = fInfo->aReply.sfFile;
-
- switch (item)
- {
- case sfHookFirstCall:
- lastFile.vRefNum = -9999;
- break;
- case sfHookLastCall:
- break;
- default:
- break;
- case sfHookNullEvent:
- if (! SameFile(&fRef,&lastFile))
- {
- lastFile = fRef;
-
- MakeCanonFSSpec(&fRef);
- GetDialogItem(theDialog, kPickFolderButton, &itemType, &itemHand, &itemBox);
- SetSFButtonTitle((ControlHandle) itemHand, &fRef, &itemBox);
- }
- break;
- case kPickFolderButton:
- fInfo->selectHit = TRUE;
- item = sfItemCancelButton;
- break;
- }
- }
- return item;
- }
-
- /*****************************************************************************/
-
- static void SetSFButtonTitle(ControlHandle theButton,FSSpec* theFile,Rect* buttonRect)
- {
- // sets the button title to <Select ā<name>ā>, but truncated to fit the rect.
-
- short width,saveSize,saveFont;
- StringHandle nStr;
- Str255 bStr;
-
- saveSize = qd.thePort->txSize;
- saveFont = qd.thePort->txFont;
-
- TextSize(12);
- TextFont(0);
-
- nStr = GetString(kStdButtonTextStrID);
- HLock((Handle) nStr);
- CopyPString(*nStr, bStr);
- HUnlock((Handle) nStr);
- ReleaseResource((Handle) nStr);
-
- width = buttonRect->right - buttonRect->left - 32 - StringWidth(bStr);
- TruncString(width, theFile->name, smTruncMiddle);
-
- ConcatPStrings(bStr,theFile->name);
- ConcatPStrings(bStr,"\pā");
-
- SetControlTitle(theButton,bStr);
- ValidRect(buttonRect);
-
- TextFont(saveFont);
- TextSize(saveSize);
- }
-
-
-